﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows.Controls;
using System.Xml.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;

namespace Twi.COMMON.WPF
{
    public class TreeViewDecorator
    {
        private TreeView _tree;

        Point _lastMouseDown;
        TreeViewItem _draggedItem, _targetItem;

        public TreeViewDecorator(TreeView tree) 
        {
            _tree = tree;
        }

        #region 拖拉
        
        public void EnableDragDrop()
        {
            _tree.AllowDrop = true;
            _tree.MouseDown += new System.Windows.Input.MouseButtonEventHandler(_tree_MouseDown);
            _tree.MouseMove += new System.Windows.Input.MouseEventHandler(_tree_MouseMove);
            _tree.DragOver += new DragEventHandler(_tree_DragOver);
            _tree.Drop += new DragEventHandler(_tree_Drop);
        }

        void _tree_Drop(object sender, DragEventArgs e)
        {
            try
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;

                // Verify that this is a valid drop and then store the drop target
                TreeViewItem TargetItem = GetNearestContainer(e.OriginalSource as UIElement);
                if (TargetItem != null && _draggedItem != null)
                {
                    _targetItem = TargetItem;
                    e.Effects = DragDropEffects.Move;

                }
            }
            catch (Exception)
            {
            }
        }

        void _tree_DragOver(object sender, DragEventArgs e)
        {
            try
            {

                Point currentPosition = e.GetPosition(_tree);


                if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
                    (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
                {
                    // Verify that this is a valid drop and then store the drop target
                    TreeViewItem item = GetNearestContainer(e.OriginalSource as UIElement);
                    if (CheckDropTarget(_draggedItem, item))
                    {
                        e.Effects = DragDropEffects.Move;
                    }
                    else
                    {
                        e.Effects = DragDropEffects.None;
                    }
                }
                e.Handled = true;
            }
            catch (Exception)
            {
            }
        }

        public event EventHandler<TreeViewDragEventArgs> DragDropCompleted;
        public event EventHandler<TreeViewDragEventArgs> Dropping;

        void _tree_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            try
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    Point currentPosition = e.GetPosition(_tree);                  

                    //如果鼠标在滚动条上的时候不进行以下操作，滚动条的宽度20为预估（假设存在滚动条的前提下）
                    if (currentPosition.X > this._tree.ActualWidth - 20 || currentPosition.Y > this._tree.ActualHeight - 20) { return; }

                    if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
                        (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
                    {
                        _draggedItem = (TreeViewItem)_tree.SelectedItem;
                        if (_draggedItem != null)
                        {
                            DragDropEffects finalDropEffect = DragDrop.DoDragDrop(_tree, _tree.SelectedValue,
                                DragDropEffects.Move);
                            //Checking target is not null and item is dragging(moving)
                            if ((finalDropEffect == DragDropEffects.Move) && (_targetItem != null))
                            {
                                // A Move drop was accepted
                                if (!_draggedItem.Header.ToString().Equals(_targetItem.Header.ToString()))
                                {
                                    CopyItem(_tree, _draggedItem, _targetItem);
                                    if (DragDropCompleted != null)
                                    {
                                        DragDropCompleted(_tree, new TreeViewDragEventArgs(_draggedItem, _targetItem));
                                    }

                                    _targetItem = null;
                                    _draggedItem = null;
                          
                                }

                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
            }
        }

        void _tree_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                _lastMouseDown = e.GetPosition(_tree);
            }
        }



        private bool CheckDropTarget(TreeViewItem sourceItem, TreeViewItem targetItem)
        {
            //Check whether the target item is meeting your condition
            bool _isEqual = false;
            if (!sourceItem.Header.ToString().Equals(targetItem.Header.ToString()))
            {
                _isEqual = true;
            }
            return _isEqual;

        }

        private void CopyItem(TreeView tree, TreeViewItem sourceItem, TreeViewItem targetItem)
        {
            //Asking user wether he want to drop the dragged TreeViewItem here or not
            if (Dropping != null) 
            {
                TreeViewDragEventArgs e = new TreeViewDragEventArgs(sourceItem, targetItem);
                Dropping(_tree, e);
                if (e.Cancel) 
                {
                    return;
                }
            }
            try
            {
                //adding dragged TreeViewItem in target TreeViewItem
                AddChild(sourceItem, targetItem);

                //finding Parent TreeViewItem of dragged TreeViewItem 
                TreeViewItem ParentItem = FindVisualParent<TreeViewItem>(sourceItem);
                // if parent is null then remove from TreeView else remove from Parent TreeViewItem
                if (ParentItem == null)
                {
                    tree.Items.Remove(sourceItem);
                }
                else
                {
                    ParentItem.Items.Remove(sourceItem);
                }
            }
            catch
            {

            }
        }

        public static void AddChild(TreeViewItem sourceItem, TreeViewItem targetItem)
        {
            // add item in target TreeViewItem 
            TreeViewItem item1 = new TreeViewItem();
            item1.Header = sourceItem.Header;
            item1.Tag = sourceItem.Tag;
            targetItem.Items.Add(item1);
            foreach (TreeViewItem item in sourceItem.Items)
            {
                AddChild(item, item1);
            }
        }
        static TObject FindVisualParent<TObject>(UIElement child) where TObject : UIElement
        {
            if (child == null)
            {
                return null;
            }

            UIElement parent = VisualTreeHelper.GetParent(child) as UIElement;

            while (parent != null)
            {
                TObject found = parent as TObject;
                if (found != null)
                {
                    return found;
                }
                else
                {
                    parent = VisualTreeHelper.GetParent(parent) as UIElement;
                }
            }

            return null;
        }
        private TreeViewItem GetNearestContainer(UIElement element)
        {
            // Walk up the element tree to the nearest tree view item.
            TreeViewItem container = element as TreeViewItem;
            while ((container == null) && (element != null))
            {
                element = VisualTreeHelper.GetParent(element) as UIElement;
                container = element as TreeViewItem;
            }
            return container;
        }

        #endregion
    }
}
